package taishan

import (
	"errors"
	"fmt"
	"github.com/rs/zerolog/log"
	"net/http"
	"runtime"
	"strings"
	"sync"
)

type HandlerFunc func(ctx *Context) error

type Engine struct {
	RouterGroup

	//方法路由
	routers Routers

	//默认路由
	defaultRoute string

	pool sync.Pool
}

func NewEngine() *Engine {
	engine := &Engine{
		RouterGroup: RouterGroup{
			prefix:      "/",
			middlewares: []HandlerFunc{},
			engine:      nil,
		},
		//groups:  RouterGroups{},
		routers: Routers{},
	}

	engine.RouterGroup.engine = engine

	//engine.groups.Set("", []*RouterGroup{&engine.RouterGroup})

	engine.pool.New = func() any {
		return DefaultContext(engine)
	}

	return engine
}

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	//获取匹配的路由
	handlers, routeNode := engine.routers.GetHandlers(req.Host, req.Method, req.URL.Path)

	//从对象池中获取
	ctx := engine.pool.Get().(*Context)
	ctx.Reset(w, req)

	//全局捕获panic异常
	ctx.handlers = []HandlerFunc{
		func(ctx *Context) error {
			defer func() {
				if err := recover(); err != nil {
					//打印堆栈信息
					// 获取当前函数的调用堆栈信息
					var pcs [32]uintptr
					n := runtime.Callers(2, pcs[:]) // 跳过两层：logErrorWithStack 和 runtime.Callers 本身
					stackTrace := strings.Builder{}
					for _, pc := range pcs[:n] {
						fn := runtime.FuncForPC(pc)
						file, line := fn.FileLine(pc)
						stackTrace.WriteString(fn.Name())
						stackTrace.WriteString(" ")
						stackTrace.WriteString(file)
						stackTrace.WriteString(":")
						stackTrace.WriteString(fmt.Sprintf("%d\n", line))
					}
					stack := stackTrace.String()

					// 使用 zerolog 打印日志，并附加堆栈信息
					Error(ctx).Str("host", ctx.Request.Host).Str("uri", ctx.Request.URL.RequestURI()).Str("stack", stack).Any("panic", err).Msg("panic recover")

					//var buf [4096]byte
					/*buf := make([]byte,4096)
					n := runtime.Stack(buf, false)
					Error(ctx).Str("host", ctx.Request.Host).Str("uri", ctx.Request.URL.RequestURI()).Any("panic", err).Msg("panic recover")
					fmt.Println(string(buf[:n]))*/
					ctx.Status(http.StatusInternalServerError)
					ctx.WriteString("system error")
					ctx.Exit()
				}
			}()
			return ctx.Next()
		},
	}

	ctx.handlers = append(ctx.handlers, handlers...)
	ctx.routeNode = routeNode

	if len(handlers) == 0 {
		//获取默认路由
		if engine.defaultRoute != "" {
			handlers, routeNode = engine.routers.GetHandlers(req.Host, "ALL", engine.defaultRoute)
		}

		if len(handlers) > 0 {
			ctx.handlers = append(ctx.handlers, handlers...)
			ctx.routeNode = routeNode
		} else {
			ctx.handlers = append(ctx.handlers, func(c *Context) error {
				//ctx.String(http.StatusNotFound, "404 NOT FOUND: %s\n", ctx.Path)
				ctx.Status(http.StatusNotFound)
				return errors.New("404 NOT FOUND: " + ctx.Path)
			})
		}
	}
	Debug(ctx).Str("host", req.Host).Str("method", req.Method).Str("path", req.URL.Path).Int("total handlers", len(ctx.handlers)).Msg("")

	ctx.Next()

	ctx.Render()
	ctx.Response = nil
	//放回对象池
	engine.pool.Put(ctx)
}

func (engine *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, engine)
}

func (engine *Engine) PrintRouter() {
	engine.routers.routers.Range(func(key, value any) bool {
		route := value.(*Router)
		fmt.Println("===", "路由表", key, "===")
		route.rootRouter.Print(0)
		return true
	})
}
func (engine *Engine) SetDefaultRoute(route string) {
	engine.defaultRoute = route
}

func logErrorWithStack() {
	// 获取当前函数的调用堆栈信息
	var pcs [32]uintptr
	n := runtime.Callers(2, pcs[:]) // 跳过两层：logErrorWithStack 和 runtime.Callers 本身
	stackTrace := strings.Builder{}
	for _, pc := range pcs[:n] {
		fn := runtime.FuncForPC(pc)
		file, line := fn.FileLine(pc)
		stackTrace.WriteString(fn.Name())
		stackTrace.WriteString(" ")
		stackTrace.WriteString(file)
		stackTrace.WriteString(":")
		stackTrace.WriteString(fmt.Sprintf("%d\n", line))
	}
	stack := stackTrace.String()

	// 使用 zerolog 打印日志，并附加堆栈信息
	log.Error().Str("stack", stack).Msg("An error occurred")
}
